﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Reflection;
using Tomahawk.Serialization;
using Microsoft.Xna.Framework;
using Tomahawk.Runtime.Resources;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Tomahawk.Serialization.SerialBoost;

namespace Tomahawk.Runtime.Timeline
{
    public abstract class Key
    {
        [XmlAttribute("Time")]
        public float time = 0;

        public Key() { }
        public Key(float time) { this.time = time; }
    }

    /*[XmlInclude(typeof(BoolTrack))]
    [XmlInclude(typeof(IntTrack))]
    [XmlInclude(typeof(FloatTrack))]
    [XmlInclude(typeof(Vector2Track))]
    [XmlInclude(typeof(Vector3Track))]
    [XmlInclude(typeof(QuaternionTrack))]
    [XmlInclude(typeof(UIControlPositionTrack))]
    [XmlInclude(typeof(UIControlRotationTrack))]
    [XmlInclude(typeof(UIControlScaleTrack))]
    [XmlInclude(typeof(UIControlAlphaTrack))]
    [XmlInclude(typeof(RefractionEffectTrack))]
    [XmlInclude(typeof(DisappearEffectTrack))]
    [XmlInclude(typeof(BlurEffectTrack))]
    [XmlInclude(typeof(ColorFilterEffectTrack))]
    [XmlInclude(typeof(TextureOffsetEffectTrack))]
    [XmlInclude(typeof(AnimatedTextureEffectTrack))]*/
    public abstract class BaseTrack : LoadableResource
    {
        public enum InterpolationMethod
        {
            Lerp,
            Spline,
        }

        [XmlAttribute("Name")]
        public string name = "";

        [XmlAttribute("Target")]
        public string targetName = "";

        [XmlAttribute("Property")]
        public string propertyName = "";

        [XmlAttribute("Interpolation")]
        public InterpolationMethod interpolation = InterpolationMethod.Lerp;

        protected Object target = null;
        protected FieldInfo fieldInfo = null;
        protected PropertyInfo propertyInfo = null;

        protected Curve LoadCurve(ContentManager contentManager, string curve)
        {
            if (curve.Length == 0)
            {
                return null;
            }

            try
            {
                return contentManager.Load<Curve>(curve);
            }
            catch
            {
                return null;
            }
        }

        public BaseTrack() { }

        public BaseTrack(string name) { this.name = name; }

        public bool Connect(Object target)
        {
            if (target == null)
            {
                return false;
            }

            Type targetType = target.GetType();

            this.target = target;

            bool hasProperty = this.propertyName != "";
            if (hasProperty)
            {
                this.fieldInfo = targetType.GetField(this.propertyName);
                this.propertyInfo = targetType.GetProperty(this.propertyName);
            }

            return (!hasProperty) || (this.fieldInfo != null) || (propertyInfo != null);
        }

        protected bool SetPropertyValue<T>(T value)
        {
            if (this.fieldInfo != null)
            {
                this.fieldInfo.SetValue(this.target, value);
                return true;
            }

            if (this.propertyInfo != null)
            {
                this.propertyInfo.SetValue(this.target, value, null);
                return true;
            }

            return false;
        }

        public virtual void OnPlayModeChanged(SyncGroup.PlayMode mode)
        {
        }

        public abstract void SortKeys();
        public abstract bool Update(float time);

        protected override bool OnLoad(GraphicsDevice graphics, ContentManager contentManager)
        {
            return true;
        }
    }

    public abstract class KeyTrack<KeyType> : BaseTrack where KeyType: Key
    {
        [XmlElement("Keys")]
        public SerializableList<KeyType> keys = new SerializableList<KeyType>();

        private int marker = 0;

        public KeyTrack() { }

        public KeyTrack(string name) : base(name) { }

        private int CompareKeysByTime(Key a, Key b)
        {
            return (int) Math.Floor(100 * (a.time - b.time));
        }

        public override void SortKeys()
        {
            this.keys.Sort(CompareKeysByTime);
        }

        protected abstract bool InterpolateLerp(KeyType lowerKey, KeyType upperKey, float alpha);
        protected abstract bool InterpolateSpline(KeyType lowerKey, KeyType upperKey, float alpha);

        public override bool Update(float time)
        {
            int maxIndex = keys.Count - 1;
            marker = Math.Min(maxIndex, Math.Max(0, marker));

            if (marker >= 0)
            {
                KeyType lowerKey = keys[marker];
                KeyType upperKey = keys[marker];

                while ((lowerKey.time > time) && (marker > 0))
                {
                    marker--;
                    upperKey = lowerKey;
                    lowerKey = keys[marker];
                }

                while ((upperKey.time < time) && (marker < maxIndex))
                {
                    marker++;
                    lowerKey = upperKey;
                    upperKey = keys[marker];
                }

                float alpha = MathHelper.Clamp((time - lowerKey.time) / ( upperKey.time - lowerKey.time), 0.0f, 1.0f);

                bool result = false;
                switch (interpolation)
                {
                    case InterpolationMethod.Lerp: result = InterpolateLerp(lowerKey, upperKey, alpha); break;
                    case InterpolationMethod.Spline: result = InterpolateSpline(lowerKey, upperKey, alpha); break;
                }
                return result;
            }

            return false;
        }
    }
}
